﻿using Chip8.Core;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Chip8.EmuApp;

public class MainGame : Game
{
    private readonly GraphicsDeviceManager? _graphics;
    private SpriteBatch? _spriteBatch;
    private readonly Machine _machine = new();
    private Bit[,]? _drawData;
    private Color[] _colors = new Color[Constants.Width * BlockSize * Constants.Height * BlockSize];
    private Texture2D? _texture;

    private const int BlockSize = 15;

    public MainGame()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
        Window.AllowUserResizing = true;
    }

    protected override void Initialize()
    {
        if (_graphics != null)
        {
            _graphics.IsFullScreen = false;
            _graphics.PreferredBackBufferWidth = Constants.Width * BlockSize;
            _graphics.PreferredBackBufferHeight = Constants.Height * BlockSize;

            _graphics.ApplyChanges();
        }

        base.Initialize();

        _texture = new Texture2D(GraphicsDevice, Constants.Width * BlockSize, Constants.Height * BlockSize);
        
        //_machine.LoadRom("ibm_logo.ch8");
        _machine.LoadRom("../../../../../../roms/programs/IBM Logo.ch8");
        _machine.Initialize();
        _machine.GraphicsUpdated += MachineOnGraphicsUpdated;
    }

    private void MachineOnGraphicsUpdated(object? sender, GraphicsUpdatedEventArgs e)
    {
        _drawData = e.Data;
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
    }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
            Keyboard.GetState().IsKeyDown(Keys.Escape))
            Exit();
        
        _machine.EmulateCycle();

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        if (_drawData is null)
        {
            return;
        }
        
        MapDrawArray(_drawData, ref _colors);
        
        _texture?.SetData(_colors);
        var drawRect = new Rectangle(0, 0, _graphics?.GraphicsDevice.Viewport.Width ?? Constants.Width * BlockSize,
            _graphics?.GraphicsDevice.Viewport.Height ??  Constants.Height * BlockSize);
        
        _spriteBatch?.Begin();
        _spriteBatch?.Draw(_texture, drawRect, Color.White);
        _spriteBatch?.End();
        

        base.Draw(gameTime);
    }

    private static void MapDrawArray(Bit[,] src, ref Color[] dst)
    {
        var dstWidth = Constants.Width * BlockSize;
        for (var y = 0; y < Constants.Height; y++)
        {
            for (var x = 0; x < Constants.Width; x++)
            {
                var val = src[x, y];
                for (var dy = 0; dy < BlockSize; dy++)
                {
                    for (var dx = 0; dx < BlockSize; dx++)
                    {
                        var dstX = x * BlockSize + dx;
                        var dstY = y * BlockSize + dy;
                        var dstIdx = dstY * dstWidth + dstX;
                        dst[dstIdx] = val ? Color.White : Color.Black;
                    }
                }
            }
        }
    }
}